﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ProceduralCity.Utility;
using ProceduralCity.Textures.Windows;

namespace ProceduralCity.Textures.Walls
{
    class SimpleWallTexture : IWallTexture
    {
        public Texture2D Texture { get; private set; }
        IWindowTexture windows;
        int chunkWidth;
        int chunkHeight;
        int chunksWide;
        int chunksHigh;

        int topFrame;
        int bottomFrame;
        int leftFrame;
        int rightFrame;

        int windowsPerChunk;
        float windowWidth;

        public void Initialize(int chunkWidth, int chunkHeight, IWindowTexture windows)
        {
            this.chunkWidth = chunkWidth;
            this.chunkHeight = chunkHeight;
            this.windows = windows;
            RandomizeLook();
        }

        public Texture2D Generate(int chunksWide, int chunksHigh)
        {
            this.chunksWide = chunksWide;
            this.chunksHigh = chunksHigh;

            // Generate the color "texture" and fill wind windows and frames
            Color[] colorTexture = new Color[(chunkWidth * chunksWide) * (chunkHeight * chunksHigh)];
            DrawChunks(colorTexture, windows);

            // Generate the Texture2D and copy from "color texture"
            Texture = new Texture2D(Game.graphics.GraphicsDevice, chunkWidth * chunksWide, chunkHeight * chunksHigh);
            Texture.SetData<Color>(colorTexture);
            return Texture;
        }



        public Rectangle GetChunk(int row, int col)
        {
            return new Rectangle(chunkWidth * col, chunkHeight * row, chunkWidth, chunkHeight);
        }
        
        void RandomizeLook()
        {
            bottomFrame = 1;
            topFrame = (int)Maths.RandomBetween(chunkHeight / 4, chunkHeight * 2 / 3);

            //int sideFrames = (int)Maths.RandomBetween(1,topFrame);
            int sideFrames = (int)Maths.RandomBetween(1,chunkHeight/4);
            leftFrame = sideFrames / 2;
            rightFrame = sideFrames - leftFrame;

            float windowRatio = Maths.RandomBetween((float)0.5, (float)1.5); // ratio of width/height
            windowsPerChunk = (int)Math.Round(windowRatio * (float)(chunkWidth - sideFrames) / (float)(chunkHeight - topFrame - bottomFrame));
            windowWidth = (chunkWidth - sideFrames) / (float)windowsPerChunk;
        }

        void DrawChunks(Color[] colorTexture, IWindowTexture windows)
        {
            for (int row = 0; row < chunksHigh; row++)
            {
                for (int col = 0; col < chunksWide; col++)
                {
                    DrawChunkWindows(colorTexture, row, col, windows);
                    DrawChunkFraming(colorTexture, row, col);
                }
            }
        }

        // Fill the texture with windows
        void DrawChunkWindows(Color[] colorTexture, int row, int col, IWindowTexture windows)
        {
            // Kelvin to RGB color mappings: http://www.vendian.org/mncharity/dir3/blackbody/
            Color[] litColors = {
                new Color(255, 165, 79),    // 2600K
                new Color(255, 173, 94),
                new Color(255, 180, 107),
                new Color(255, 187, 120),
                new Color(255, 193, 132),
                new Color(255, 199, 143),
                new Color(255, 204, 153),
                new Color(255, 209, 163),
                new Color(255, 213, 173),
                new Color(255, 217, 182),
                new Color(255, 221, 190),
                new Color(255, 225, 198),
                new Color(255, 228, 206),
                new Color(255, 232, 213),
                new Color(255, 235, 220),
                new Color(255, 238, 227),
                new Color(255, 240, 233),
                new Color(255, 243, 239),
                new Color(255, 245, 245),
                new Color(255, 248, 251),
                new Color(254, 249, 255),  // 6600K
                new Color(249, 246, 255),
                new Color(245, 243, 255),
                new Color(240, 241, 255),
                new Color(237, 239, 255),
                new Color(233, 237, 255),
                new Color(230, 235, 255)};  // 7800K

            if (Maths.RandomBetween(0, 1) < 0.25)
            {
                Vector3 litColor = litColors[(int)Utility.Maths.RandomBetween(0, litColors.Length)].ToVector3();
                litColor.X *= (float)Utility.Maths.RandomBetween((float)1.1, (float)0.9);
                litColor.Y *= (float)Utility.Maths.RandomBetween((float)1.1, (float)0.9);
                litColor.Z *= (float)Utility.Maths.RandomBetween((float)1.1, (float)0.5);
                
                FillWindow(colorTexture, row, col, new Color(litColor));
            }
            else
            {
                FillWindow(colorTexture, row, col, new Color(32, 32, 32));
            }
        }

        // Fill the contents of a window
        void FillWindow(Color[] colorTexture, int row, int col, Color color)
        {
            for (int r = topFrame; r < chunkHeight - bottomFrame; ++r)
            {
                for (int c = 0; c <chunkWidth; ++c)
                {
                    SetChunkPixel(colorTexture, row, col, r, c, color);
                }
            }
        }

        // Draw the frames for every window in the texture
        void DrawFrames(Color[] colorTexture)
        {
            for (int row = 0; row < chunksHigh; row++)
            {
                for (int col = 0; col < chunksWide; col++)
                {
                    DrawChunkFraming(colorTexture, row, col);
                }
            }
        }

        // Given a chunk, draw the frames
        void DrawChunkFraming(Color[] colorTexture, int row, int col)
        {
            // Draw top frame
            for (int r = 0; r < topFrame; r++)
            {
                for (int c = 0; c < chunkWidth; c++)
                {
                    SetChunkPixel(colorTexture, row, col, r, c, Color.Black);
                }
            }

            // Draw bottom frame
            for (int r = 0; r < bottomFrame; r++)
            {
                for (int c = 0; c < chunkWidth; c++)
                {
                    SetChunkPixel(colorTexture, row, col, chunkHeight - 1 - r, c, new Color(10,10,10));
                }
            }

            // Draw frame left and right side
            for (int r = 0; r < chunkHeight; r++)
            {
                for (int c = 0; c < leftFrame; c++)
                {
                    SetChunkPixel(colorTexture, row, col, r, c, Color.Black);
                }
                for (int c = 0; c < rightFrame; c++)
                {
                    SetChunkPixel(colorTexture, row, col, r, chunkWidth - c - 1, Color.Black);
                }
            }

            // Draw Window frames
            for (int w = 1; w < windowsPerChunk; w++)
            {
                for (int r = 0; r < chunkHeight; r++)
                {
                    SetChunkPixel(colorTexture, row, col, r, (int)Math.Round(leftFrame + w * windowWidth), Color.Black);
                }
            }
        }

        // Given a window (chunk), set a pixel within the window
        void SetChunkPixel(Color[] colorTexture, int windowRow, int windowCol, int row, int col, Color color)
        {
            SetTexturePixel(colorTexture, windowRow * chunkHeight + row, windowCol * chunkWidth + col, color);
        }

        // Given a colorTexture and pixel, set the color
        void SetTexturePixel(Color[] colorTexture, int row, int col, Color color)
        {
            colorTexture[row * (chunksWide*chunkWidth) + col] = color;
        }
    }
}
